;############################################################################ 

; Считывание данных о символах и количестве вхождений символов в результирующий (декомпрессированный) файл
; Аргумент:
;			[esp + 4] - FILE* исходного (сжатого) файла
; Возвращаемое значение:
;			al - результат выполнения (0 - успех, > 0 - неудача)
;			code - массив вхождений для каждого символа
;			[tree_size] - число различных символов, входящий в файл

read_table_symbol_enters_from_file:

	; Сохраняем данные о стеке
	push ebp
	mov ebp,esp

	; esi указывает на буфер, использующийся для чтения
	mov esi,o_buffer

	; edi содержит адрес дескриптора сжатого файла
	mov edi,[ebp + 8]

	; Считываем очередной блок информации
	push edi
	push dword io_buffer_size
	push dword 1
	push esi
	call fread

	add esp,16

	; Если ничего не удалось прочитать - ошибка
	cmp eax,1
	jnl RTSENFF_not_read_error
		mov al,RTSENFF_read_file_too_small
		jmp short RTSENFF_end
	RTSENFF_not_read_error:

	; После операции чтения необходимо выставить индикатор позиции файла в нужное положение
	; (то есть - на начало сжатого содержимого)
	; Получаем количество разных символов, входящих в декомпрессированный файл
	movzx eax,word [esi]
	; Счетчик в буфере ввода увеличиваем на 2
	inc esi
	inc esi
	push eax
	xor edx,edx
	; Это количество умножаем на 5 (1 байт - код символа + 4 байта - количество вхождений)
	mov ecx,5
	mul ecx
	; И добавляем 2 - размер поля, хранящего количество разных символов, входящих в декомпрессированный файл
	add eax,2

	; Выставлять индикатор будем от начала
	push dword [SEEK_SET]
	push eax
	push edi
	call fseek

	add esp,12
	pop eax

	; Читаем количество различных (уникальных) символов в результирующем файле

	; ecx - счетчик цикла
	mov ecx,eax
	mov [tree_size],ax

	RTSENFF_next_sym:
		; Считываем очередные пять байт
		; Код символа
		movzx ebx,byte [esi]
		inc esi
		; Количество вхождений символа
		mov eax,[esi]
		bswap eax
		add esi,4
		; Записываем количество вхождений текущего символа в массив code
		; Индекс элемента = код символа * 4
		shl bx,2
		mov [code + ebx],eax
	loop RTSENFF_next_sym

	; Вернуть ноль в случае успеха
	xor al,al

	RTSENFF_end:

	; Восстанавливаем стек
	mov esp,ebp
	pop ebp

	ret

;############################################################################ 

; Декомпрессия исходного файла
; Аргументы:
;			[esp + 4] - FILE* исходного (сжатого) файла
;			file_write - указатель на строку, содержащую имя результирующего (декомпрессированного) файла
;			[root] - адрес корня дерева Хаффмана
; Возвращаемое значение:
;			al - результат выполнения (0 - успех, > 0 - неудача)
;			результирующий файл - результат декомпрессии

decompress_file:

	; Сохраняем данные о стеке
	push ebp
	mov ebp,esp

	; Открываем результирующий файл
	push mode_write
	push dword [file_write]
	call fopen
	test eax,eax
	jnz DF_open_write_no_error
		mov al,DF_open_write_error
		jmp DF_write_no_close
	DF_open_write_no_error:

	add esp,8

	; Переменные, хранящие дескрипторы исходного и результирующего файлов
	%define i_file [ebp + 8]
	%define o_file [ebp - 4]
	; Переменная, хранящая размер результирующего файла
	%define o_file_size [ebp - 8]

	; Непосредственное создание локальной переменной, хранящей дескриптор результирующего файла
	push eax

	; В ebx - текущий корень дерева
	; Фактически, ebx - "маркер" обхода дерева
	mov ebx,[root]
	; Непосредственное создание локальной переменной, хранящей размер результирующего файла
	; (поле elem_num_dis у корня дерева)
	push dword [ebx + elem_num_dis]

	; edx - текущий размер в байтах декомпрессированного файла
	xor edx,edx
	; edi - текущее смещение в результирующем буфере
	xor edi,edi

	DF_next_page:
		push edx
		
		; Считаем очередную порцию информации из сжатого файла во
		; входной буфер
		push dword i_file
		push dword io_buffer_size
		push dword 1
		push dword i_buffer
		call fread

		add esp,16
		pop edx
		
		; Если было считано меньше одного байта - то либо произошла ошибка,
		; либо достигнут конец сжатого файла. В любом случае - завершим цикл
		; декомпрессии
		cmp eax,1
		jnl DF_not_end_loop_1
			jmp DF_end_loop
		DF_not_end_loop_1:

		; esi - смещение в буфере, хранящем сжатые данные
		xor esi,esi

		DF_next_symb:
			; Обработаем очередной байт сжатых данных

			; Считаем очередной байт сжатых данных
			mov al,[i_buffer + esi]

			; Будем обрабатывать сжатые данные побитно
			mov ecx,8
			
			DF_next_bit:
				; В случае, если у корня нет сыновей - не будем переходить к поддеревам
				cmp [ebx + elem_left_dis],dword 0
				jz DF_end_if
					; Получаем значение очередного бита
					shl ax,1
					test ah,1
					; Если бит равен нулю - переходим к левому поддереву, иначе - к правому
					jnz DF_right
						mov ebx,[ebx + elem_left_dis]
						jmp short DF_end_if
					DF_right:
						mov ebx,[ebx + elem_right_dis]
				DF_end_if:

				push ax

				; Если адрес левого поддерева у текущего узла есть ноль, то этот узел суть есть сток - запишем
				; символ соответствующий этому стоку в результирующий буфер
				mov eax,[ebx + elem_left_dis]
				test eax,eax
				jnz DF_not_stok
					mov al,[ebx + elem_code_dis]
					mov [o_buffer + edi],al
					; Счетчики количества символов в результирующем буфере и результирующем файле
					; увеличим на единицу
					inc edi
					inc edx
					cmp edi,io_buffer_size
					jnz DF_not_flush
						; Если результирующий буфер заполнен - сбросим его содержимое в результирующий
						; файл
						push edx
						push ecx

						push dword o_file
						push dword io_buffer_size
						push dword 1
						push dword o_buffer
						call fwrite
						
						add esp,16
						pop ecx
						pop edx
						
						; Проверим, произошла ли ошибка при записи
						cmp eax,io_buffer_size
						jz DF_write_no_error_1
							mov al,DF_write_error_1
							jmp short DF_end
						DF_write_no_error_1:
						
						; Обнулим смещение в результирующем буфере
						xor edi,edi
					DF_not_flush:

					; Если результирующий файл полностью заполнен - выйдем из цикла декомпрессии
					cmp edx,o_file_size
					jz DF_end_loop

					; Вернуть ebx обратно на вершину
					mov ebx,[root]
				DF_not_stok:

				pop ax
			loop DF_next_bit

			; Перейдем к следующему байту буфера с сжатым содержимым, если только данных буфер
			; не пройден полностью
			inc esi
			cmp esi,io_buffer_size
		jnz DF_next_symb
	jmp DF_next_page

	DF_end_loop:

	; Проверим, произошла ли при операции чтения ошибка
	push edx

	push dword i_file
	call ferror
	test eax,eax
	jz DF_read_no_error
		mov al,DF_read_error
		jmp short DF_end
	DF_read_no_error:
	
	mov edx,[esp + 4]

	; Проверим, все ли байты результирующего файла декомпрессированы
	; (т.о. производится проверка на то, правильные ли данные о размере
	; декомпрессированного файла содержаться в корне дерева)
	cmp edx,o_file_size
	jz DF_full_decompress_file
		mov al,DF_decompress_file_size_error
		jmp short DF_end
	DF_full_decompress_file:

	; Запишем последную порцию информации в декомпрессированный файл
	push dword o_file
	push edi
	push dword 1
	push dword o_buffer
	call fwrite

	add esp,16

	; Проверим, произошла ли ошибка при записи
	cmp eax,edi
	jz DF_write_no_error_2
		mov al,DF_write_error_2
		jmp short DF_end
	DF_write_no_error_2:

	xor al,al

	DF_end:

	push eax

	; Закрываем результирующий файл
	push dword o_file
	call fclose
	test eax,eax
	jz DF_close_write_no_error
		mov al,DF_close_write_error
		jmp short DF_write_no_close
	DF_close_write_no_error:

	mov eax,[esp + 4]

	DF_write_no_close:
	
	; Восстанавливаем стек
	mov esp,ebp
	pop ebp

	ret

;############################################################################ 

